using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using Common.Shared.Design;
using System.Collections;
using Common.Shared.Reflection;
using System.Reflection;

namespace Common.Shared.Xml.Wddx {
	[WddxRoot ( "wddxPacket" )]
	public class WddxPacket : WddxObject {
		private string _version = "1.0";
		private WddxHeader _header;
		private WddxData _data;

		public WddxPacket ( ) {
			this.Data = new WddxData ( );
			this.Header = new WddxHeader ( "Generated with Wddx.net" );
		}

		[WddxElement ( "data" ), WddxOrder ( 1 )]
		public WddxData Data {
			get { return _data; }
			set { _data = value; }
		}

		[WddxElement ( "header" ), WddxOrder ( 0 )]
		public WddxHeader Header {
			get { return _header; }
			set { _header = value; }
		}

		[WddxAttribute ( "version" )]
		public string Version {
			get { return _version; }
			set { _version = value; }
		}

		public object ToObject ( ) {
			return ToObject<object> ( );
		}

		public T ToObject<T> ( ) {
			return default ( T );
		}

		/// <summary>
		/// Creates a WddxPacket from any object
		/// </summary>
		/// <param name="obj">The obj.</param>
		/// <returns></returns>
		public static WddxPacket FromObject ( object obj ) {

			WddxPacket packet = new WddxPacket ( );
			Type type = obj.GetType ( );

			if ( !TypeUtils.CanSerialize ( type ) ) {
				throw new WddxException ( "Can not serialize object to Wddx Packet. No default constructor defined." );
			}
			packet.Data.Value = CreateDataTypeFromObject ( obj );

			return packet;
		}


		private static WddxDataTypeObject CreateDataTypeFromObject ( object obj ) {
			WddxDataTypeObject result = null;
			Type type = null;
			try {
				if ( obj != null ) {
					type = obj.GetType ( );
				}
				if ( obj == null || ( TypeUtils.IsNullable ( type ) && ( obj as Nullable ) == null ) ) {
					result = new WddxNull ( );
				} else if ( type.IsSubclassOf ( typeof ( WddxDataTypeObject ) ) ) {
					result = ( obj ) as WddxDataTypeObject;
				} else if ( TypeUtils.IsValueType ( type ) ) {
					// primitive
					if ( type == typeof ( String ) ) {
						string s = (string)obj;
						if ( string.IsNullOrEmpty ( s ) ) {
							result = new WddxNull ( );
						} else {
							result = new WddxString ( s );
						}
					} else if ( TypeUtils.IsNumeric ( type ) ) {
						if ( type == typeof ( IntPtr ) ) {
							result = new WddxNumber ( Convert.ToDouble ( TypeUtils.GetIntPtrValue ( (IntPtr)obj ) ) );
						} else if ( type == typeof ( UIntPtr ) ) {
							result = new WddxNumber ( Convert.ToDouble ( TypeUtils.GetUIntPtrValue ( (UIntPtr)obj ) ) );
						} else {
							result = new WddxNumber ( Convert.ToDouble ( obj ) );
						}
					} else if ( type == typeof ( Boolean ) ) {
						result = new WddxBoolean ( (bool)obj );
					} else if ( type == typeof ( Guid ) ) {
						Guid g = (Guid)obj;
						result = new WddxString ( g.ToString ( "D" ) );
					} else {
						Console.WriteLine ( "Unhandled ValueType: {0}", type.Name );

					}
				} else if ( type.IsArray || TypeUtils.IsCollection ( type ) ) {
					// array
					result = new WddxArray ( );
					foreach ( object var in (IEnumerable)obj ) {
						( result as WddxArray ).Items.Add ( CreateDataTypeFromObject ( var ) );
					}

				} else {
					//object
					result = new WddxStructure ( );

					foreach ( MemberInfo mi in MemberUtils.GetPublicFieldsAndProperties ( type ) ) {
						// try to stop "loop"
						if ( ( string.Compare ( "parent", mi.Name, true ) == 0 || string.Compare ( "owner", mi.Name, true ) == 0 ) && MemberUtils.GetMemberInfoType ( mi ) == type ) {
							continue;
						}
						//try {
							object val = MemberUtils.GetMemberInfoValue<object> ( obj, mi );
							if ( val != null ) {
								( result as WddxStructure ).Variables.Add ( new WddxVariable ( mi.Name, CreateDataTypeFromObject ( val ) ) );
							}
						//} catch ( Exception ex1 ) {
						
						//}
					}
				}
			} catch ( Exception ex ) {
				//Console.WriteLine ( ex.ToString ( ) );
			}
			result.Type = type != null ? string.Format ( "{1},{0}", type.Assembly.GetName().FullName, type.FullName ) : string.Empty;
			return result;
		}
	}
}
